// Package httplongclinet Keepalive
package httplongclinet

import (
	"crypto/tls"
	"net"
	"net/http"
	"sync"
	"time"

	"gitee.com/zhaobohan/stress-testing/model"
	"golang.org/x/net/http2"
)

var (
	mutex sync.RWMutex
	// clients 客户端
	// key 客户端id - value 客户端
	clients = make(map[uint64]*http.Client)
)

// NewClient new
func NewClient(i uint64, request *model.Request) *http.Client {
	client := getClient(i)
	if client != nil {
		return client
	}
	return setClient(i, request)
}

func getClient(i uint64) *http.Client {
	mutex.RLock()
	defer mutex.RUnlock()
	return clients[i]
}

func setClient(i uint64, request *model.Request) *http.Client {
	mutex.Lock()
	defer mutex.Unlock()
	client := createLangHTTPClient(request)
	clients[i] = client
	return client
}

// createLangHTTPClient 初始化长连接客户端参数
// 创建了一个配置了长连接的 HTTP 客户端传输对象
func createLangHTTPClient(request *model.Request) *http.Client {
	tr := &http.Transport{
		// 使用 net.Dialer 来建立 TCP 连接
		// Timeout 设置为 30 秒，表示如果连接在 30 秒内没有建立成功，则超时
		// KeepAlive 设置为 30 秒，表示连接建立后，如果 30 秒内没有数据传输，则发送一个 keep-alive 探测包以保持连接
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:        0,                // 最大连接数,默认0无穷大
		MaxIdleConnsPerHost: request.MaxCon,   // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
		IdleConnTimeout:     90 * time.Second, // 多长时间未使用自动关闭连接
		// InsecureSkipVerify 设置为 true，表示不验证服务器的 SSL 证书
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	if request.HTTP2 {
		// 使用真实证书 验证证书 模拟真实请求
		tr = &http.Transport{
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).DialContext,
			MaxIdleConns:        0,                // 最大连接数,默认0无穷大
			MaxIdleConnsPerHost: request.MaxCon,   // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
			IdleConnTimeout:     90 * time.Second, // 多长时间未使用自动关闭连接
			// 配置 TLS 客户端设置，InsecureSkipVerify 设置为 false，表示验证服务器的 SSL 证书
			TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
		}
		// 将 tr 配置为支持 HTTP/2 协议
		_ = http2.ConfigureTransport(tr)
	}

	client := &http.Client{
		Transport: tr,
	}

	// 禁止 HTTP 客户端自动重定向，而是让客户端在遇到重定向时停止并返回最后一个响应
	if !request.Redirect {
		client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		}
	}

	return client
}
